package evaluation;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import mulan.classifier.MultiLabelOutput;
import mulan.evaluation.measure.MacroAverageMeasure;
import mulan.evaluation.measure.MacroFMeasure;
import mulan.evaluation.measure.MacroPrecision;
import mulan.evaluation.measure.MacroRecall;
import mulan.evaluation.measure.MacroSpecificity;
import mulan.evaluation.measure.Measure;
import mulan.evaluation.measure.MicroFMeasure;
import mulan.evaluation.measure.MicroPrecision;
import mulan.evaluation.measure.MicroRecall;
import mulan.evaluation.measure.MicroSpecificity;


public class PerformanceEvaluation {

	private double run_time_ns;
	private double build_time_ns,classify_time_ns;
	private List<Measure> measures;
	private int num_train_instances,num_test_instances;
	private double true_labels,predicted_labels;
	private String classifier;
	private String train_file,test_file;
	private List<String> labels;

	public PerformanceEvaluation(double run_time_ns,double build_time_ns, double classify_time_ns,
			List<Measure> measures, int num_train_instances,
			int num_test_intstances, double true_labels,
			double predicted_labels, String classifier,
			String train_file, String test_file,List<String> labels) {
		super();
		this.run_time_ns = run_time_ns;
		this.build_time_ns = build_time_ns;
		this.classify_time_ns = classify_time_ns;
		this.measures = measures;
		this.num_train_instances = num_train_instances;
		this.num_test_instances = num_test_intstances;
		this.true_labels = true_labels;
		this.predicted_labels = predicted_labels;
		this.classifier = classifier;
		this.train_file = train_file;
		this.test_file = test_file;
		this.labels = labels;
	}

	public PerformanceEvaluation() {
	}

	public void setRun_time_ns(double run_time_ns) {
		this.run_time_ns = run_time_ns;
	}

	public void setBuild_time_ns(double build_time_ns) {
		this.build_time_ns = build_time_ns;
	}

	public void setClassify_time_ns(double classify_time_ns) {
		this.classify_time_ns = classify_time_ns;
	}

	public void setMeasures(List<Measure> measures) {
		this.measures = measures;
	}

	public void setNum_train_instances(int num_train_instances) {
		this.num_train_instances = num_train_instances;
	}

	public void setNum_test_instances(int num_test_instances) {
		this.num_test_instances = num_test_instances;
	}

	public void setTrue_labels(double true_labels) {
		this.true_labels = true_labels;
	}

	public void setPredicted_labels(double predicted_labels) {
		this.predicted_labels = predicted_labels;
	}

	public void setClassifier(String classifier) {
		this.classifier = classifier;
	}

	public void setTrain_file(String train_file) {
		this.train_file = train_file;
	}

	public void setTest_file(String test_file) {
		this.test_file = test_file;
	}

	public void setLabels(List<String> labels) {
		this.labels = labels;
	}

	public void flushToFile(String file) {
		PrintWriter out;
		try {
			out = new PrintWriter(file);
			out.print(toString());
			out.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("RESULTS FOR MULTI LABEL CLASSIFICATION WITH\n");
		sb.append("Classifier: "+classifier+"\n");
		sb.append("On datasets: train-"+train_file+" and test-"+test_file+"\n");
		sb.append("Num instances: train-"+num_train_instances+" and test-"+num_test_instances+"\n");
		sb.append("\n");
		sb.append("Total run time: "+run_time_ns/1000000000+" s \n");
		sb.append("Time for building the model: "+build_time_ns/1000000000+" s \n");
		sb.append("Time for classifiying (sum): "+classify_time_ns/1000000000+" s \n");
		sb.append("Time for classifiying (single instance): "+classify_time_ns/num_test_instances/1000000+" ms \n");
		sb.append("\n");
		for (Measure m : measures) {
			sb.append(m);
			if (m instanceof MacroAverageMeasure) {
				sb.append("\n");
				int i = 0;
				for (String label : labels) {
					sb.append(label).append(": ").append(((MacroAverageMeasure) m).getValue(i++)).append(" ");
				}
			}
			sb.append("\n");
		}
		sb.append("\n");
		sb.append("Guessed label_count:"+predicted_labels+"\n");
		sb.append("True label_count:"+true_labels+"\n");
		sb.append("Avg Guessed label_count per instance:"+predicted_labels/num_test_instances+"\n");
		sb.append("Avg True label_count per instance:"+true_labels/num_test_instances+"\n");
		sb.append("Ratio between guessed and predicted labels:"+predicted_labels/true_labels+"\n");
		return sb.toString();
	}

	public void update(MultiLabelOutput mlo, boolean[] truelabels) {
		//WARNING THIS MAY NEED TO BE UNCOMMENTED
		//mlo = new MultiLabelOutput(mlo.getConfidences(), .1);
		/*
		if ( Math.random() < .1 )
			System.out.println(Arrays.toString(mlo.getConfidences()));
			*/
		for ( Measure m : measures )
			m.update(mlo, truelabels);
		predicted_labels += getTrueCount(mlo.getBipartition());
		true_labels += getTrueCount(truelabels);
	}

	private double getTrueCount(boolean[] bipartition) {
		int res = 0;
		for ( boolean b : bipartition ) 
			res += b?1:0;
		return res;
	}

	public List<Measure> prepareMeasures(int numOfLabels) {
		List<Measure> measures = new ArrayList<Measure>();
		measures.add(new MicroPrecision(numOfLabels));
		measures.add(new MicroRecall(numOfLabels));
		measures.add(new MicroFMeasure(numOfLabels));
		measures.add(new MicroSpecificity(numOfLabels));
		measures.add(new MacroPrecision(numOfLabels));
		measures.add(new MacroRecall(numOfLabels));
		measures.add(new MacroFMeasure(numOfLabels));
		measures.add(new MacroSpecificity(numOfLabels));
		return measures;
	}


}
